feat: add browser client runtime for HMR - #2323
Conversation
Ports the browser client into `client-src/`, mirroring the layout used
in `webpack-dev-server` (source in `client-src/`, built to `client/`).
The client connects to the SSE endpoint via `EventSource`, parses
query-string options from `__resourceQuery`, dispatches `building`,
`built` and `sync` payloads, applies HMR through `process-update.js`
and renders compile-time errors and warnings through an in-page overlay
(`overlay.js`).
Exposed via the `./client` subpath export so users can wire it as a
webpack entry: `require('webpack-dev-middleware/client')`. The source
is transpiled with a browser-targeted babel override and the resulting
files are shipped under `/client`.
Covers the public client API and key SSE handling paths in jsdom: EventSource connection on default and custom paths, ignored heartbeat messages, dispatch of building/built/sync to subscribers, custom handler for unknown actions, warnings on invalid JSON, EventSource wrapper caching across multiple entries, and timeout-driven reconnect.
Adds tests covering the original webpack-hot-middleware client suite (processUpdate invocations on built/sync, errored/warning behavior, overlay show/hide transitions, the overlayWarnings option, the name filter), while keeping the new coverage for heartbeat handling, invalid JSON warnings, EventSource wrapper caching across entries and timeout-driven reconnects.
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## hot-middleware #2323 +/- ##
===============================================
Coverage 92.70% 92.70%
===============================================
Files 3 3
Lines 1001 1001
Branches 311 311
===============================================
Hits 928 928
Misses 65 65
Partials 8 8 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
The ported logic called `module.hot.check`/`apply` with both a callback and a Promise-handling branch to support webpack < 2. In webpack 5 both paths fire, so the callback ran twice, triggering a redundant `module.hot.apply` on every update. Drop the legacy callback path and use the Promise API exclusively, which is the canonical webpack 5 contract and matches our peer dependency.
Mirrors the layout webpack-dev-server uses: a separate `tsconfig.client.json` (`noEmit`, browser-targeted libs, `webpack/module` augmentation) runs over `client-src/` via a new `lint:types-client` script, with a small `client-src/globals.d.ts` declaring `ansi-html-community` and the per-page singletons the client stores on `window`. Refines the JSDoc annotations in `client-src/index.js` and `client-src/process-update.js` so `module.hot`, `window` extensions and the HMR `ApplyOptions` type-check cleanly.
Adds a 'Hot Module Replacement client' section explaining how to wire `webpack-dev-middleware/client` as a webpack entry, the query-string options that the runtime understands, and the programmatic `subscribe` / `subscribeAll` / `useCustomOverlay` / `setOptionsAndConnect` exports.
Switches the client-src lint config to the dedicated preset `eslint-config-webpack` ships for browser-targeted CommonJS code, the same family of preset webpack-dev-server uses for its own client. Brings the per-directory rule overrides down to two: `no-console` (legitimately used for HMR status messages) and `no-use-before-define` (relaxed for hoisted function declarations). Adjusts the source to satisfy the rest of the preset directly: adds `use strict` headers, fills in JSDoc for every function, renames `EventSourceWrapper` to `createEventSourceWrapper` (per `new-cap`), names the anonymous module exports, and reorders `performReload` before `handleError` so it is declared before use.
Wraps `webpack/lib/logging/runtime` in a small `utils/log.js` module that exposes a level-based logger registered under the `webpack-dev-middleware` name (matching the infrastructure logger the server side already uses). Replaces every `console.log`/ `console.warn` call in the client and HMR update path with the equivalent `log.info`/`log.warn`/`log.error` calls so output is prefixed and gated by a single `logging` level. User-facing API: - `logging` query-string option accepts `none|error|warn|info|log|verbose` - The previous `log`, `warn`, `noInfo` and `quiet` flags are dropped in favour of `logging` Other cleanups enabled by this: - Drop the `no-console: off` exception from the client-src ESLint config - Update README's client option table accordingly - Add tests covering the new `logging` levels and the logger prefix
Replaces regex-based `some(([msg]) => /.../).toBe(true)` checks on the mocked console with `toMatchSnapshot()` over `mock.calls`. The snapshots capture the exact log lines including the `[webpack-dev-middleware]` prefix and per-call argument count, so any change to the log format surfaces in the test output instead of silently passing. Adds explicit assertions to the existing error / warning flow tests so `console.error` / `console.warn` mocks are not only silenced but also verified to be called with the expected output.
`eslint-config-webpack@4.9.6` still ships `browser-outdated-recommended-commonjs` with `configs["javascript/es5"]` and no parser override, so `const` is rejected. The module variant of the same preset patches this upstream — we replicate the patch locally until the commonjs variant does the same.
* feat: add browser client runtime for HMR
Ports the browser client into `client-src/`, mirroring the layout used
in `webpack-dev-server` (source in `client-src/`, built to `client/`).
The client connects to the SSE endpoint via `EventSource`, parses
query-string options from `__resourceQuery`, dispatches `building`,
`built` and `sync` payloads, applies HMR through `process-update.js`
and renders compile-time errors and warnings through an in-page overlay
(`overlay.js`).
Exposed via the `./client` subpath export so users can wire it as a
webpack entry: `require('webpack-dev-middleware/client')`. The source
is transpiled with a browser-targeted babel override and the resulting
files are shipped under `/client`.
* test: add browser client runtime tests
Covers the public client API and key SSE handling paths in jsdom:
EventSource connection on default and custom paths, ignored heartbeat
messages, dispatch of building/built/sync to subscribers, custom
handler for unknown actions, warnings on invalid JSON, EventSource
wrapper caching across multiple entries, and timeout-driven reconnect.
* test: expand browser client coverage to mirror webpack-hot-middleware
Adds tests covering the original webpack-hot-middleware client suite
(processUpdate invocations on built/sync, errored/warning behavior,
overlay show/hide transitions, the overlayWarnings option, the name
filter), while keeping the new coverage for heartbeat handling,
invalid JSON warnings, EventSource wrapper caching across entries and
timeout-driven reconnects.
* ci: run on push and PRs against the hot-middleware umbrella branch
* refactor(client): switch process-update to promise-only HMR API
The ported logic called `module.hot.check`/`apply` with both a callback
and a Promise-handling branch to support webpack < 2. In webpack 5 both
paths fire, so the callback ran twice, triggering a redundant
`module.hot.apply` on every update.
Drop the legacy callback path and use the Promise API exclusively, which
is the canonical webpack 5 contract and matches our peer dependency.
* chore(client): type-check client-src with a dedicated tsconfig
Mirrors the layout webpack-dev-server uses: a separate
`tsconfig.client.json` (`noEmit`, browser-targeted libs,
`webpack/module` augmentation) runs over `client-src/` via a new
`lint:types-client` script, with a small `client-src/globals.d.ts`
declaring `ansi-html-community` and the per-page singletons the client
stores on `window`.
Refines the JSDoc annotations in `client-src/index.js` and
`client-src/process-update.js` so `module.hot`, `window` extensions
and the HMR `ApplyOptions` type-check cleanly.
* docs: document the browser client runtime in README
Adds a 'Hot Module Replacement client' section explaining how to wire
`webpack-dev-middleware/client` as a webpack entry, the query-string
options that the runtime understands, and the programmatic
`subscribe` / `subscribeAll` / `useCustomOverlay` /
`setOptionsAndConnect` exports.
* chore(client): adopt browser-outdated-recommended-commonjs eslint preset
Switches the client-src lint config to the dedicated preset
`eslint-config-webpack` ships for browser-targeted CommonJS code, the
same family of preset webpack-dev-server uses for its own client.
Brings the per-directory rule overrides down to two:
`no-console` (legitimately used for HMR status messages) and
`no-use-before-define` (relaxed for hoisted function declarations).
Adjusts the source to satisfy the rest of the preset directly: adds
`use strict` headers, fills in JSDoc for every function, renames
`EventSourceWrapper` to `createEventSourceWrapper` (per `new-cap`),
names the anonymous module exports, and reorders `performReload`
before `handleError` so it is declared before use.
* refactor(client): route logging through webpack's runtime logger
Wraps `webpack/lib/logging/runtime` in a small `utils/log.js` module
that exposes a level-based logger registered under the
`webpack-dev-middleware` name (matching the infrastructure logger the
server side already uses). Replaces every `console.log`/
`console.warn` call in the client and HMR update path with the
equivalent `log.info`/`log.warn`/`log.error` calls so output is
prefixed and gated by a single `logging` level.
User-facing API:
- `logging` query-string option accepts `none|error|warn|info|log|verbose`
- The previous `log`, `warn`, `noInfo` and `quiet` flags are dropped
in favour of `logging`
Other cleanups enabled by this:
- Drop the `no-console: off` exception from the client-src ESLint config
- Update README's client option table accordingly
- Add tests covering the new `logging` levels and the logger prefix
* test(client): use snapshots for logger output assertions
Replaces regex-based `some(([msg]) => /.../).toBe(true)` checks on the
mocked console with `toMatchSnapshot()` over `mock.calls`. The
snapshots capture the exact log lines including the
`[webpack-dev-middleware]` prefix and per-call argument count, so any
change to the log format surfaces in the test output instead of silently
passing.
Adds explicit assertions to the existing error / warning flow tests so
`console.error` / `console.warn` mocks are not only silenced but also
verified to be called with the expected output.
* chore: document why client-src needs the ecmaVersion override
`eslint-config-webpack@4.9.6` still ships `browser-outdated-recommended-commonjs`
with `configs["javascript/es5"]` and no parser override, so `const`
is rejected. The module variant of the same preset patches this
upstream — we replicate the patch locally until the commonjs variant
does the same.
* refactor(client): migrate to ES modules and update Babel configuration
* fixup!
|
@bjohansebas Please don't merge such things in future without approving, at least 1 approve, I don't make it strict to be more flexibility, but it doesn't mean we should ignore it, I don't merge then because we need to change to architecture problems on webpack and dev server side |
|
this isn't merged into |
|
Yeah, I see it, just for future, with other branches will be good to make the right architecture too, but it was already merged, so we will work with branch together, I don't like it because it makes a big diff and often unreadable |
* feat: add browser client runtime for HMR
Ports the browser client into `client-src/`, mirroring the layout used
in `webpack-dev-server` (source in `client-src/`, built to `client/`).
The client connects to the SSE endpoint via `EventSource`, parses
query-string options from `__resourceQuery`, dispatches `building`,
`built` and `sync` payloads, applies HMR through `process-update.js`
and renders compile-time errors and warnings through an in-page overlay
(`overlay.js`).
Exposed via the `./client` subpath export so users can wire it as a
webpack entry: `require('webpack-dev-middleware/client')`. The source
is transpiled with a browser-targeted babel override and the resulting
files are shipped under `/client`.
* test: add browser client runtime tests
Covers the public client API and key SSE handling paths in jsdom:
EventSource connection on default and custom paths, ignored heartbeat
messages, dispatch of building/built/sync to subscribers, custom
handler for unknown actions, warnings on invalid JSON, EventSource
wrapper caching across multiple entries, and timeout-driven reconnect.
* test: expand browser client coverage to mirror webpack-hot-middleware
Adds tests covering the original webpack-hot-middleware client suite
(processUpdate invocations on built/sync, errored/warning behavior,
overlay show/hide transitions, the overlayWarnings option, the name
filter), while keeping the new coverage for heartbeat handling,
invalid JSON warnings, EventSource wrapper caching across entries and
timeout-driven reconnects.
* ci: run on push and PRs against the hot-middleware umbrella branch
* refactor(client): switch process-update to promise-only HMR API
The ported logic called `module.hot.check`/`apply` with both a callback
and a Promise-handling branch to support webpack < 2. In webpack 5 both
paths fire, so the callback ran twice, triggering a redundant
`module.hot.apply` on every update.
Drop the legacy callback path and use the Promise API exclusively, which
is the canonical webpack 5 contract and matches our peer dependency.
* chore(client): type-check client-src with a dedicated tsconfig
Mirrors the layout webpack-dev-server uses: a separate
`tsconfig.client.json` (`noEmit`, browser-targeted libs,
`webpack/module` augmentation) runs over `client-src/` via a new
`lint:types-client` script, with a small `client-src/globals.d.ts`
declaring `ansi-html-community` and the per-page singletons the client
stores on `window`.
Refines the JSDoc annotations in `client-src/index.js` and
`client-src/process-update.js` so `module.hot`, `window` extensions
and the HMR `ApplyOptions` type-check cleanly.
* docs: document the browser client runtime in README
Adds a 'Hot Module Replacement client' section explaining how to wire
`webpack-dev-middleware/client` as a webpack entry, the query-string
options that the runtime understands, and the programmatic
`subscribe` / `subscribeAll` / `useCustomOverlay` /
`setOptionsAndConnect` exports.
* chore(client): adopt browser-outdated-recommended-commonjs eslint preset
Switches the client-src lint config to the dedicated preset
`eslint-config-webpack` ships for browser-targeted CommonJS code, the
same family of preset webpack-dev-server uses for its own client.
Brings the per-directory rule overrides down to two:
`no-console` (legitimately used for HMR status messages) and
`no-use-before-define` (relaxed for hoisted function declarations).
Adjusts the source to satisfy the rest of the preset directly: adds
`use strict` headers, fills in JSDoc for every function, renames
`EventSourceWrapper` to `createEventSourceWrapper` (per `new-cap`),
names the anonymous module exports, and reorders `performReload`
before `handleError` so it is declared before use.
* refactor(client): route logging through webpack's runtime logger
Wraps `webpack/lib/logging/runtime` in a small `utils/log.js` module
that exposes a level-based logger registered under the
`webpack-dev-middleware` name (matching the infrastructure logger the
server side already uses). Replaces every `console.log`/
`console.warn` call in the client and HMR update path with the
equivalent `log.info`/`log.warn`/`log.error` calls so output is
prefixed and gated by a single `logging` level.
User-facing API:
- `logging` query-string option accepts `none|error|warn|info|log|verbose`
- The previous `log`, `warn`, `noInfo` and `quiet` flags are dropped
in favour of `logging`
Other cleanups enabled by this:
- Drop the `no-console: off` exception from the client-src ESLint config
- Update README's client option table accordingly
- Add tests covering the new `logging` levels and the logger prefix
* test(client): use snapshots for logger output assertions
Replaces regex-based `some(([msg]) => /.../).toBe(true)` checks on the
mocked console with `toMatchSnapshot()` over `mock.calls`. The
snapshots capture the exact log lines including the
`[webpack-dev-middleware]` prefix and per-call argument count, so any
change to the log format surfaces in the test output instead of silently
passing.
Adds explicit assertions to the existing error / warning flow tests so
`console.error` / `console.warn` mocks are not only silenced but also
verified to be called with the expected output.
* chore: document why client-src needs the ecmaVersion override
`eslint-config-webpack@4.9.6` still ships `browser-outdated-recommended-commonjs`
with `configs["javascript/es5"]` and no parser override, so `const`
is rejected. The module variant of the same preset patches this
upstream — we replicate the patch locally until the commonjs variant
does the same.
* refactor(client): migrate to ES modules and update Babel configuration
* fixup!
* feat: add browser client runtime for HMR
Ports the browser client into `client-src/`, mirroring the layout used
in `webpack-dev-server` (source in `client-src/`, built to `client/`).
The client connects to the SSE endpoint via `EventSource`, parses
query-string options from `__resourceQuery`, dispatches `building`,
`built` and `sync` payloads, applies HMR through `process-update.js`
and renders compile-time errors and warnings through an in-page overlay
(`overlay.js`).
Exposed via the `./client` subpath export so users can wire it as a
webpack entry: `require('webpack-dev-middleware/client')`. The source
is transpiled with a browser-targeted babel override and the resulting
files are shipped under `/client`.
* test: add browser client runtime tests
Covers the public client API and key SSE handling paths in jsdom:
EventSource connection on default and custom paths, ignored heartbeat
messages, dispatch of building/built/sync to subscribers, custom
handler for unknown actions, warnings on invalid JSON, EventSource
wrapper caching across multiple entries, and timeout-driven reconnect.
* test: expand browser client coverage to mirror webpack-hot-middleware
Adds tests covering the original webpack-hot-middleware client suite
(processUpdate invocations on built/sync, errored/warning behavior,
overlay show/hide transitions, the overlayWarnings option, the name
filter), while keeping the new coverage for heartbeat handling,
invalid JSON warnings, EventSource wrapper caching across entries and
timeout-driven reconnects.
* ci: run on push and PRs against the hot-middleware umbrella branch
* refactor(client): switch process-update to promise-only HMR API
The ported logic called `module.hot.check`/`apply` with both a callback
and a Promise-handling branch to support webpack < 2. In webpack 5 both
paths fire, so the callback ran twice, triggering a redundant
`module.hot.apply` on every update.
Drop the legacy callback path and use the Promise API exclusively, which
is the canonical webpack 5 contract and matches our peer dependency.
* chore(client): type-check client-src with a dedicated tsconfig
Mirrors the layout webpack-dev-server uses: a separate
`tsconfig.client.json` (`noEmit`, browser-targeted libs,
`webpack/module` augmentation) runs over `client-src/` via a new
`lint:types-client` script, with a small `client-src/globals.d.ts`
declaring `ansi-html-community` and the per-page singletons the client
stores on `window`.
Refines the JSDoc annotations in `client-src/index.js` and
`client-src/process-update.js` so `module.hot`, `window` extensions
and the HMR `ApplyOptions` type-check cleanly.
* docs: document the browser client runtime in README
Adds a 'Hot Module Replacement client' section explaining how to wire
`webpack-dev-middleware/client` as a webpack entry, the query-string
options that the runtime understands, and the programmatic
`subscribe` / `subscribeAll` / `useCustomOverlay` /
`setOptionsAndConnect` exports.
* chore(client): adopt browser-outdated-recommended-commonjs eslint preset
Switches the client-src lint config to the dedicated preset
`eslint-config-webpack` ships for browser-targeted CommonJS code, the
same family of preset webpack-dev-server uses for its own client.
Brings the per-directory rule overrides down to two:
`no-console` (legitimately used for HMR status messages) and
`no-use-before-define` (relaxed for hoisted function declarations).
Adjusts the source to satisfy the rest of the preset directly: adds
`use strict` headers, fills in JSDoc for every function, renames
`EventSourceWrapper` to `createEventSourceWrapper` (per `new-cap`),
names the anonymous module exports, and reorders `performReload`
before `handleError` so it is declared before use.
* refactor(client): route logging through webpack's runtime logger
Wraps `webpack/lib/logging/runtime` in a small `utils/log.js` module
that exposes a level-based logger registered under the
`webpack-dev-middleware` name (matching the infrastructure logger the
server side already uses). Replaces every `console.log`/
`console.warn` call in the client and HMR update path with the
equivalent `log.info`/`log.warn`/`log.error` calls so output is
prefixed and gated by a single `logging` level.
User-facing API:
- `logging` query-string option accepts `none|error|warn|info|log|verbose`
- The previous `log`, `warn`, `noInfo` and `quiet` flags are dropped
in favour of `logging`
Other cleanups enabled by this:
- Drop the `no-console: off` exception from the client-src ESLint config
- Update README's client option table accordingly
- Add tests covering the new `logging` levels and the logger prefix
* test(client): use snapshots for logger output assertions
Replaces regex-based `some(([msg]) => /.../).toBe(true)` checks on the
mocked console with `toMatchSnapshot()` over `mock.calls`. The
snapshots capture the exact log lines including the
`[webpack-dev-middleware]` prefix and per-call argument count, so any
change to the log format surfaces in the test output instead of silently
passing.
Adds explicit assertions to the existing error / warning flow tests so
`console.error` / `console.warn` mocks are not only silenced but also
verified to be called with the expected output.
* chore: document why client-src needs the ecmaVersion override
`eslint-config-webpack@4.9.6` still ships `browser-outdated-recommended-commonjs`
with `configs["javascript/es5"]` and no parser override, so `const`
is rejected. The module variant of the same preset patches this
upstream — we replicate the patch locally until the commonjs variant
does the same.
* refactor(client): migrate to ES modules and update Babel configuration
* fixup!
* feat: add browser client runtime for HMR
Ports the browser client into `client-src/`, mirroring the layout used
in `webpack-dev-server` (source in `client-src/`, built to `client/`).
The client connects to the SSE endpoint via `EventSource`, parses
query-string options from `__resourceQuery`, dispatches `building`,
`built` and `sync` payloads, applies HMR through `process-update.js`
and renders compile-time errors and warnings through an in-page overlay
(`overlay.js`).
Exposed via the `./client` subpath export so users can wire it as a
webpack entry: `require('webpack-dev-middleware/client')`. The source
is transpiled with a browser-targeted babel override and the resulting
files are shipped under `/client`.
* test: add browser client runtime tests
Covers the public client API and key SSE handling paths in jsdom:
EventSource connection on default and custom paths, ignored heartbeat
messages, dispatch of building/built/sync to subscribers, custom
handler for unknown actions, warnings on invalid JSON, EventSource
wrapper caching across multiple entries, and timeout-driven reconnect.
* test: expand browser client coverage to mirror webpack-hot-middleware
Adds tests covering the original webpack-hot-middleware client suite
(processUpdate invocations on built/sync, errored/warning behavior,
overlay show/hide transitions, the overlayWarnings option, the name
filter), while keeping the new coverage for heartbeat handling,
invalid JSON warnings, EventSource wrapper caching across entries and
timeout-driven reconnects.
* ci: run on push and PRs against the hot-middleware umbrella branch
* refactor(client): switch process-update to promise-only HMR API
The ported logic called `module.hot.check`/`apply` with both a callback
and a Promise-handling branch to support webpack < 2. In webpack 5 both
paths fire, so the callback ran twice, triggering a redundant
`module.hot.apply` on every update.
Drop the legacy callback path and use the Promise API exclusively, which
is the canonical webpack 5 contract and matches our peer dependency.
* chore(client): type-check client-src with a dedicated tsconfig
Mirrors the layout webpack-dev-server uses: a separate
`tsconfig.client.json` (`noEmit`, browser-targeted libs,
`webpack/module` augmentation) runs over `client-src/` via a new
`lint:types-client` script, with a small `client-src/globals.d.ts`
declaring `ansi-html-community` and the per-page singletons the client
stores on `window`.
Refines the JSDoc annotations in `client-src/index.js` and
`client-src/process-update.js` so `module.hot`, `window` extensions
and the HMR `ApplyOptions` type-check cleanly.
* docs: document the browser client runtime in README
Adds a 'Hot Module Replacement client' section explaining how to wire
`webpack-dev-middleware/client` as a webpack entry, the query-string
options that the runtime understands, and the programmatic
`subscribe` / `subscribeAll` / `useCustomOverlay` /
`setOptionsAndConnect` exports.
* chore(client): adopt browser-outdated-recommended-commonjs eslint preset
Switches the client-src lint config to the dedicated preset
`eslint-config-webpack` ships for browser-targeted CommonJS code, the
same family of preset webpack-dev-server uses for its own client.
Brings the per-directory rule overrides down to two:
`no-console` (legitimately used for HMR status messages) and
`no-use-before-define` (relaxed for hoisted function declarations).
Adjusts the source to satisfy the rest of the preset directly: adds
`use strict` headers, fills in JSDoc for every function, renames
`EventSourceWrapper` to `createEventSourceWrapper` (per `new-cap`),
names the anonymous module exports, and reorders `performReload`
before `handleError` so it is declared before use.
* refactor(client): route logging through webpack's runtime logger
Wraps `webpack/lib/logging/runtime` in a small `utils/log.js` module
that exposes a level-based logger registered under the
`webpack-dev-middleware` name (matching the infrastructure logger the
server side already uses). Replaces every `console.log`/
`console.warn` call in the client and HMR update path with the
equivalent `log.info`/`log.warn`/`log.error` calls so output is
prefixed and gated by a single `logging` level.
User-facing API:
- `logging` query-string option accepts `none|error|warn|info|log|verbose`
- The previous `log`, `warn`, `noInfo` and `quiet` flags are dropped
in favour of `logging`
Other cleanups enabled by this:
- Drop the `no-console: off` exception from the client-src ESLint config
- Update README's client option table accordingly
- Add tests covering the new `logging` levels and the logger prefix
* test(client): use snapshots for logger output assertions
Replaces regex-based `some(([msg]) => /.../).toBe(true)` checks on the
mocked console with `toMatchSnapshot()` over `mock.calls`. The
snapshots capture the exact log lines including the
`[webpack-dev-middleware]` prefix and per-call argument count, so any
change to the log format surfaces in the test output instead of silently
passing.
Adds explicit assertions to the existing error / warning flow tests so
`console.error` / `console.warn` mocks are not only silenced but also
verified to be called with the expected output.
* chore: document why client-src needs the ecmaVersion override
`eslint-config-webpack@4.9.6` still ships `browser-outdated-recommended-commonjs`
with `configs["javascript/es5"]` and no parser override, so `const`
is rejected. The module variant of the same preset patches this
upstream — we replicate the patch locally until the commonjs variant
does the same.
* refactor(client): migrate to ES modules and update Babel configuration
* fixup!
* feat: add browser client runtime for HMR
Ports the browser client into `client-src/`, mirroring the layout used
in `webpack-dev-server` (source in `client-src/`, built to `client/`).
The client connects to the SSE endpoint via `EventSource`, parses
query-string options from `__resourceQuery`, dispatches `building`,
`built` and `sync` payloads, applies HMR through `process-update.js`
and renders compile-time errors and warnings through an in-page overlay
(`overlay.js`).
Exposed via the `./client` subpath export so users can wire it as a
webpack entry: `require('webpack-dev-middleware/client')`. The source
is transpiled with a browser-targeted babel override and the resulting
files are shipped under `/client`.
* test: add browser client runtime tests
Covers the public client API and key SSE handling paths in jsdom:
EventSource connection on default and custom paths, ignored heartbeat
messages, dispatch of building/built/sync to subscribers, custom
handler for unknown actions, warnings on invalid JSON, EventSource
wrapper caching across multiple entries, and timeout-driven reconnect.
* test: expand browser client coverage to mirror webpack-hot-middleware
Adds tests covering the original webpack-hot-middleware client suite
(processUpdate invocations on built/sync, errored/warning behavior,
overlay show/hide transitions, the overlayWarnings option, the name
filter), while keeping the new coverage for heartbeat handling,
invalid JSON warnings, EventSource wrapper caching across entries and
timeout-driven reconnects.
* ci: run on push and PRs against the hot-middleware umbrella branch
* refactor(client): switch process-update to promise-only HMR API
The ported logic called `module.hot.check`/`apply` with both a callback
and a Promise-handling branch to support webpack < 2. In webpack 5 both
paths fire, so the callback ran twice, triggering a redundant
`module.hot.apply` on every update.
Drop the legacy callback path and use the Promise API exclusively, which
is the canonical webpack 5 contract and matches our peer dependency.
* chore(client): type-check client-src with a dedicated tsconfig
Mirrors the layout webpack-dev-server uses: a separate
`tsconfig.client.json` (`noEmit`, browser-targeted libs,
`webpack/module` augmentation) runs over `client-src/` via a new
`lint:types-client` script, with a small `client-src/globals.d.ts`
declaring `ansi-html-community` and the per-page singletons the client
stores on `window`.
Refines the JSDoc annotations in `client-src/index.js` and
`client-src/process-update.js` so `module.hot`, `window` extensions
and the HMR `ApplyOptions` type-check cleanly.
* docs: document the browser client runtime in README
Adds a 'Hot Module Replacement client' section explaining how to wire
`webpack-dev-middleware/client` as a webpack entry, the query-string
options that the runtime understands, and the programmatic
`subscribe` / `subscribeAll` / `useCustomOverlay` /
`setOptionsAndConnect` exports.
* chore(client): adopt browser-outdated-recommended-commonjs eslint preset
Switches the client-src lint config to the dedicated preset
`eslint-config-webpack` ships for browser-targeted CommonJS code, the
same family of preset webpack-dev-server uses for its own client.
Brings the per-directory rule overrides down to two:
`no-console` (legitimately used for HMR status messages) and
`no-use-before-define` (relaxed for hoisted function declarations).
Adjusts the source to satisfy the rest of the preset directly: adds
`use strict` headers, fills in JSDoc for every function, renames
`EventSourceWrapper` to `createEventSourceWrapper` (per `new-cap`),
names the anonymous module exports, and reorders `performReload`
before `handleError` so it is declared before use.
* refactor(client): route logging through webpack's runtime logger
Wraps `webpack/lib/logging/runtime` in a small `utils/log.js` module
that exposes a level-based logger registered under the
`webpack-dev-middleware` name (matching the infrastructure logger the
server side already uses). Replaces every `console.log`/
`console.warn` call in the client and HMR update path with the
equivalent `log.info`/`log.warn`/`log.error` calls so output is
prefixed and gated by a single `logging` level.
User-facing API:
- `logging` query-string option accepts `none|error|warn|info|log|verbose`
- The previous `log`, `warn`, `noInfo` and `quiet` flags are dropped
in favour of `logging`
Other cleanups enabled by this:
- Drop the `no-console: off` exception from the client-src ESLint config
- Update README's client option table accordingly
- Add tests covering the new `logging` levels and the logger prefix
* test(client): use snapshots for logger output assertions
Replaces regex-based `some(([msg]) => /.../).toBe(true)` checks on the
mocked console with `toMatchSnapshot()` over `mock.calls`. The
snapshots capture the exact log lines including the
`[webpack-dev-middleware]` prefix and per-call argument count, so any
change to the log format surfaces in the test output instead of silently
passing.
Adds explicit assertions to the existing error / warning flow tests so
`console.error` / `console.warn` mocks are not only silenced but also
verified to be called with the expected output.
* chore: document why client-src needs the ecmaVersion override
`eslint-config-webpack@4.9.6` still ships `browser-outdated-recommended-commonjs`
with `configs["javascript/es5"]` and no parser override, so `const`
is rejected. The module variant of the same preset patches this
upstream — we replicate the patch locally until the commonjs variant
does the same.
* refactor(client): migrate to ES modules and update Babel configuration
* fixup!
* feat: add browser client runtime for HMR
Ports the browser client into `client-src/`, mirroring the layout used
in `webpack-dev-server` (source in `client-src/`, built to `client/`).
The client connects to the SSE endpoint via `EventSource`, parses
query-string options from `__resourceQuery`, dispatches `building`,
`built` and `sync` payloads, applies HMR through `process-update.js`
and renders compile-time errors and warnings through an in-page overlay
(`overlay.js`).
Exposed via the `./client` subpath export so users can wire it as a
webpack entry: `require('webpack-dev-middleware/client')`. The source
is transpiled with a browser-targeted babel override and the resulting
files are shipped under `/client`.
* test: add browser client runtime tests
Covers the public client API and key SSE handling paths in jsdom:
EventSource connection on default and custom paths, ignored heartbeat
messages, dispatch of building/built/sync to subscribers, custom
handler for unknown actions, warnings on invalid JSON, EventSource
wrapper caching across multiple entries, and timeout-driven reconnect.
* test: expand browser client coverage to mirror webpack-hot-middleware
Adds tests covering the original webpack-hot-middleware client suite
(processUpdate invocations on built/sync, errored/warning behavior,
overlay show/hide transitions, the overlayWarnings option, the name
filter), while keeping the new coverage for heartbeat handling,
invalid JSON warnings, EventSource wrapper caching across entries and
timeout-driven reconnects.
* ci: run on push and PRs against the hot-middleware umbrella branch
* refactor(client): switch process-update to promise-only HMR API
The ported logic called `module.hot.check`/`apply` with both a callback
and a Promise-handling branch to support webpack < 2. In webpack 5 both
paths fire, so the callback ran twice, triggering a redundant
`module.hot.apply` on every update.
Drop the legacy callback path and use the Promise API exclusively, which
is the canonical webpack 5 contract and matches our peer dependency.
* chore(client): type-check client-src with a dedicated tsconfig
Mirrors the layout webpack-dev-server uses: a separate
`tsconfig.client.json` (`noEmit`, browser-targeted libs,
`webpack/module` augmentation) runs over `client-src/` via a new
`lint:types-client` script, with a small `client-src/globals.d.ts`
declaring `ansi-html-community` and the per-page singletons the client
stores on `window`.
Refines the JSDoc annotations in `client-src/index.js` and
`client-src/process-update.js` so `module.hot`, `window` extensions
and the HMR `ApplyOptions` type-check cleanly.
* docs: document the browser client runtime in README
Adds a 'Hot Module Replacement client' section explaining how to wire
`webpack-dev-middleware/client` as a webpack entry, the query-string
options that the runtime understands, and the programmatic
`subscribe` / `subscribeAll` / `useCustomOverlay` /
`setOptionsAndConnect` exports.
* chore(client): adopt browser-outdated-recommended-commonjs eslint preset
Switches the client-src lint config to the dedicated preset
`eslint-config-webpack` ships for browser-targeted CommonJS code, the
same family of preset webpack-dev-server uses for its own client.
Brings the per-directory rule overrides down to two:
`no-console` (legitimately used for HMR status messages) and
`no-use-before-define` (relaxed for hoisted function declarations).
Adjusts the source to satisfy the rest of the preset directly: adds
`use strict` headers, fills in JSDoc for every function, renames
`EventSourceWrapper` to `createEventSourceWrapper` (per `new-cap`),
names the anonymous module exports, and reorders `performReload`
before `handleError` so it is declared before use.
* refactor(client): route logging through webpack's runtime logger
Wraps `webpack/lib/logging/runtime` in a small `utils/log.js` module
that exposes a level-based logger registered under the
`webpack-dev-middleware` name (matching the infrastructure logger the
server side already uses). Replaces every `console.log`/
`console.warn` call in the client and HMR update path with the
equivalent `log.info`/`log.warn`/`log.error` calls so output is
prefixed and gated by a single `logging` level.
User-facing API:
- `logging` query-string option accepts `none|error|warn|info|log|verbose`
- The previous `log`, `warn`, `noInfo` and `quiet` flags are dropped
in favour of `logging`
Other cleanups enabled by this:
- Drop the `no-console: off` exception from the client-src ESLint config
- Update README's client option table accordingly
- Add tests covering the new `logging` levels and the logger prefix
* test(client): use snapshots for logger output assertions
Replaces regex-based `some(([msg]) => /.../).toBe(true)` checks on the
mocked console with `toMatchSnapshot()` over `mock.calls`. The
snapshots capture the exact log lines including the
`[webpack-dev-middleware]` prefix and per-call argument count, so any
change to the log format surfaces in the test output instead of silently
passing.
Adds explicit assertions to the existing error / warning flow tests so
`console.error` / `console.warn` mocks are not only silenced but also
verified to be called with the expected output.
* chore: document why client-src needs the ecmaVersion override
`eslint-config-webpack@4.9.6` still ships `browser-outdated-recommended-commonjs`
with `configs["javascript/es5"]` and no parser override, so `const`
is rejected. The module variant of the same preset patches this
upstream — we replicate the patch locally until the commonjs variant
does the same.
* refactor(client): migrate to ES modules and update Babel configuration
* fixup!
* feat: add hot option for hot module replacement * feat: add browser client runtime for HMR (#2323) * feat: add browser client runtime for HMR Ports the browser client into `client-src/`, mirroring the layout used in `webpack-dev-server` (source in `client-src/`, built to `client/`). The client connects to the SSE endpoint via `EventSource`, parses query-string options from `__resourceQuery`, dispatches `building`, `built` and `sync` payloads, applies HMR through `process-update.js` and renders compile-time errors and warnings through an in-page overlay (`overlay.js`). Exposed via the `./client` subpath export so users can wire it as a webpack entry: `require('webpack-dev-middleware/client')`. The source is transpiled with a browser-targeted babel override and the resulting files are shipped under `/client`. * test: add browser client runtime tests Covers the public client API and key SSE handling paths in jsdom: EventSource connection on default and custom paths, ignored heartbeat messages, dispatch of building/built/sync to subscribers, custom handler for unknown actions, warnings on invalid JSON, EventSource wrapper caching across multiple entries, and timeout-driven reconnect. * test: expand browser client coverage to mirror webpack-hot-middleware Adds tests covering the original webpack-hot-middleware client suite (processUpdate invocations on built/sync, errored/warning behavior, overlay show/hide transitions, the overlayWarnings option, the name filter), while keeping the new coverage for heartbeat handling, invalid JSON warnings, EventSource wrapper caching across entries and timeout-driven reconnects. * ci: run on push and PRs against the hot-middleware umbrella branch * refactor(client): switch process-update to promise-only HMR API The ported logic called `module.hot.check`/`apply` with both a callback and a Promise-handling branch to support webpack < 2. In webpack 5 both paths fire, so the callback ran twice, triggering a redundant `module.hot.apply` on every update. Drop the legacy callback path and use the Promise API exclusively, which is the canonical webpack 5 contract and matches our peer dependency. * chore(client): type-check client-src with a dedicated tsconfig Mirrors the layout webpack-dev-server uses: a separate `tsconfig.client.json` (`noEmit`, browser-targeted libs, `webpack/module` augmentation) runs over `client-src/` via a new `lint:types-client` script, with a small `client-src/globals.d.ts` declaring `ansi-html-community` and the per-page singletons the client stores on `window`. Refines the JSDoc annotations in `client-src/index.js` and `client-src/process-update.js` so `module.hot`, `window` extensions and the HMR `ApplyOptions` type-check cleanly. * docs: document the browser client runtime in README Adds a 'Hot Module Replacement client' section explaining how to wire `webpack-dev-middleware/client` as a webpack entry, the query-string options that the runtime understands, and the programmatic `subscribe` / `subscribeAll` / `useCustomOverlay` / `setOptionsAndConnect` exports. * chore(client): adopt browser-outdated-recommended-commonjs eslint preset Switches the client-src lint config to the dedicated preset `eslint-config-webpack` ships for browser-targeted CommonJS code, the same family of preset webpack-dev-server uses for its own client. Brings the per-directory rule overrides down to two: `no-console` (legitimately used for HMR status messages) and `no-use-before-define` (relaxed for hoisted function declarations). Adjusts the source to satisfy the rest of the preset directly: adds `use strict` headers, fills in JSDoc for every function, renames `EventSourceWrapper` to `createEventSourceWrapper` (per `new-cap`), names the anonymous module exports, and reorders `performReload` before `handleError` so it is declared before use. * refactor(client): route logging through webpack's runtime logger Wraps `webpack/lib/logging/runtime` in a small `utils/log.js` module that exposes a level-based logger registered under the `webpack-dev-middleware` name (matching the infrastructure logger the server side already uses). Replaces every `console.log`/ `console.warn` call in the client and HMR update path with the equivalent `log.info`/`log.warn`/`log.error` calls so output is prefixed and gated by a single `logging` level. User-facing API: - `logging` query-string option accepts `none|error|warn|info|log|verbose` - The previous `log`, `warn`, `noInfo` and `quiet` flags are dropped in favour of `logging` Other cleanups enabled by this: - Drop the `no-console: off` exception from the client-src ESLint config - Update README's client option table accordingly - Add tests covering the new `logging` levels and the logger prefix * test(client): use snapshots for logger output assertions Replaces regex-based `some(([msg]) => /.../).toBe(true)` checks on the mocked console with `toMatchSnapshot()` over `mock.calls`. The snapshots capture the exact log lines including the `[webpack-dev-middleware]` prefix and per-call argument count, so any change to the log format surfaces in the test output instead of silently passing. Adds explicit assertions to the existing error / warning flow tests so `console.error` / `console.warn` mocks are not only silenced but also verified to be called with the expected output. * chore: document why client-src needs the ecmaVersion override `eslint-config-webpack@4.9.6` still ships `browser-outdated-recommended-commonjs` with `configs["javascript/es5"]` and no parser override, so `const` is rejected. The module variant of the same preset patches this upstream — we replicate the patch locally until the commonjs variant does the same. * refactor(client): migrate to ES modules and update Babel configuration * fixup! * feat: implement hot module replacement middleware (#2321) * fix(test): correct output path typo in webpack.array.warning fixture The first compiler entry used `../../outputs/...` which escaped the test directory and wrote artifacts to the repository root, outside of the `/test/outputs` paths covered by `.gitignore` and `.prettierignore`. * feat: implement hot module replacement middleware Adds a `hot: true | { path, heartbeat, log, statsOptions }` option that turns the dev middleware into a Server-Sent Events endpoint publishing `building`, `built` and `sync` payloads from the webpack compiler. The hot endpoint defaults to `/__webpack_hmr` and is served by the existing middleware - no separate `app.use()` call is required. `close()` tears down clients and the heartbeat timer. * test: add tests for hot middleware Covers schema validation of the `hot` option (success and failure cases with snapshots), unit tests for `pathMatch`, `formatErrors`, `buildModuleMap` and `createEventStream`, and integration tests that verify SSE headers, the default and custom hot paths, MultiCompiler support, `close()` teardown, and the `log` option (custom function and `log: false`). * test: add unit and integration tests for hot middleware functionality * feat: enhance honoWrapper to support Web ReadableStream for hot middleware responses * feat: add TypeScript definitions for hot module replacement functionality * test(hot): cover publish, sync-on-connect, headers and close behavior Ports the remaining unit-level cases from webpack-hot-middleware that were not already covered by the framework matrix in middleware.test.js: - the public `publish()` API broadcasts custom payloads - a client connecting after a build receives a `sync` event initialised from the last stats - HTTP/1 clients get `Connection: keep-alive`, HTTP/2 clients do not - when `stats.name` is empty the published payload falls back to `compilation.name` - a single broadcast reaches every attached client - after `close()` further compiler events do not produce writes * docs: document the hot option in README * docs: list the hot option in the README options table * refactor: replace EXPECTED_ANY with specific types in hot module definitions * docs: update README to clarify default stats options for SSE payload * refactor: remove log option from hot middleware and update related documentation * docs: update default value for hot option in README to false * docs: add hot module replacement example with Express server and client setup * feat: enhance error overlay with close button and improved styling * feat: enable reload option for HMR by default and update tests * feat: add SSE helper functions and tests for event streaming * refactor(client): inline ansi stripping and drop the strip-ansi dependency (#2350) The client runtime runs in the browser, so Node's util.stripVTControlCharacters is not an option. Inline the ansi-regex pattern (verified against strip-ansi@6 output) in a small util instead of shipping the dependency. Ref webpack/webpack-hot-middleware#465 Ref webpack/webpack-hot-middleware#474 * refactor(hot): drop the module map from the SSE payload (#2349) webpack-dev-server does not send module names over the wire: the HMR runtime logs module ids on apply. Align the SSE payload with that (name, action, time, hash, errors, warnings) instead of serializing a module id → name map that was only used for log cosmetics and was empty with the default stats options anyway. Ref webpack/webpack-hot-middleware#452 Ref webpack/webpack-hot-middleware#306 * feat(client): add disconnect() to close the SSE connection (#2351) * feat(client): add disconnect() to close the SSE connection Expose a way to close the EventSource for the current path and stop the reconnection watchdog (e.g. before tearing the page down). The cached wrapper is dropped so a later setOptionsAndConnect() opens a fresh connection. Ref webpack/webpack-hot-middleware#367 * fixup! * feat(hot): include the changed file in the building event (#2352) The compiler's invalid hook reports which file invalidated the compilation. Forward it as an optional `file` field on the `building` payload and log it in the client, so users can see what triggered a rebuild. Ref webpack/webpack-hot-middleware#173 * feat(client): bring the error overlay to parity with webpack-dev-server (#2353) * feat(client): bring the error overlay to parity with webpack-dev-server - Render inside an about:blank iframe and style exclusively through the CSSOM so the overlay works under a strict style-src CSP; inline styles from ansi-html are re-applied via style.cssText. - Support Trusted Types: innerHTML writes go through a policy (configurable via overlayTrustedTypesPolicyName). - Capture uncaught runtime errors and unhandled rejections in the overlay (overlayRuntimeErrors, default true), with the same React error boundary heuristic as dev-server. - Inline the HTML entity encoder and drop the html-entities dependency. - Expose the overlay as a standalone subpath export (webpack-dev-middleware/client/overlay) so webpack-dev-server can reuse it. Ref webpack/webpack-hot-middleware#457 * feat(client): support opening file references in the editor When `overlayOpenEditorEndpoint` is set, file chips in the overlay become clickable and issue GET <endpoint>?fileName=<file:line:column> — the same contract as webpack-dev-server's open-editor route. The endpoint implementation is left to the server integration, so no launch-editor dependency is added. * feat(client): bring the error overlay to parity with webpack-dev-server - Render inside an about:blank iframe and style exclusively through the CSSOM so the overlay works under a strict style-src CSP; inline styles from ansi-html are re-applied via style.cssText. - Support Trusted Types: innerHTML writes go through a policy (configurable via the overlay's trustedTypesPolicyName). - Capture uncaught runtime errors and unhandled rejections in the overlay, with the same React error boundary heuristic as dev-server. - Adopt dev-server's client.overlay option shape: a boolean or a JSON object with errors/warnings/runtimeErrors (booleans or filter functions) and trustedTypesPolicyName, so dev-server configs migrate as-is. Warnings are shown by default and no longer block updates, matching dev-server. - Support opening file references in the editor: when overlayOpenEditorEndpoint is set, file chips issue GET <endpoint>?fileName=<file:line:column> (same contract as dev-server's open-editor route); the endpoint implementation is left to the server integration. - Inline the HTML entity encoder and drop the html-entities dependency. - Expose the overlay as a standalone subpath export (webpack-dev-middleware/client/overlay) so webpack-dev-server can reuse it. Ref webpack/webpack-hot-middleware#457 Ref webpack/webpack-hot-middleware#184 * fix(client): track overlay problems per compilation With a MultiCompiler the client receives one event per bundle, and a successful build from one bundle used to wipe another bundle's still-valid errors from the overlay. Keep the live problems keyed by compilation name, render the union, and only clear the overlay when every compilation is clean. The console de-duplication cache is also keyed per bundle so interleaved payloads do not defeat it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(examples): add a MultiCompiler hot example Keep examples/hot as the minimal single-compiler setup (now with runtime-error buttons and an opt-in DEMO_WARNING build warning), and add examples/hot-multi-compiler: two compilers ("app" and "admin") sharing one middleware instance and a single SSE connection, with per-bundle `?name=` client scoping and recipes for the overlay's per-compilation error tracking. Each multi config sets a distinct output.uniqueName (and prefixed hot-update filenames): with both bundles on one page, a shared webpackHotUpdate global would make each bundle's updates land in the wrong runtime. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(client): nest overlay extensions inside the overlay option Move styles, ansiColors and openEditorEndpoint into the overlay object as webpack-dev-middleware extensions of dev-server's client.overlay shape, replacing the flat overlayStyles/ansiColors/ overlayOpenEditorEndpoint options. --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * test(client): cover process-update Isolate the HMR runtime lookup behind a small util so process-update is loadable under jest, and add coverage for the updated-modules console output. * feat(client): collapse the updated-modules list in the console (#2361) The success path now logs a one-line summary with the module count at the default "info" level, and the per-module detail as a collapsed console group visible from the "log" level up (webpack's runtime logger gates groups below that level). The unaccepted-modules warning list stays flat: it is diagnostic output for the failing case. Ref webpack/webpack-hot-middleware#311 * feat(client): paginate the overlay problems (#2359) The overlay now shows one problem at a time, with a header row holding the problem badge and prev/next navigation (colored after the problem type), a page counter and arrow-key navigation. Each new problem set starts at its first page, accumulated runtime errors land on the newest one, and identical re-published sets (e.g. another bundle of a multi-compiler syncing) keep the current page. Enabled by default; disable with the overlay option's `paginate` extension (`?overlay={"paginate":false}`) to render the full list. Also ignore click targets re-rendered away mid-dispatch in the backdrop-dismiss listener, which otherwise closed the overlay when clicking the navigation buttons. * fix(client): avoid double slash when joining dynamicPublicPath (#2348) * fix(client): avoid double slash when joining dynamicPublicPath When output.publicPath ends with a slash (the common case) and the SSE path starts with one, the naive concatenation produced URLs like `https://host//__webpack_hmr`, which never match the server-side pathname check. Ref webpack/webpack-hot-middleware#366 * fix(client): append path to the public path like a filename in dynamicPublicPath The default `path` starts with "/", so the naive concatenation with a public path ending in "/" produced URLs like `https://host//__webpack_hmr`, which never match the server-side pathname check. Strip the leading slash from `path` and concatenate without any other normalization: the public path itself is left untouched (intentional double slashes, e.g. nginx rewrites, are preserved) and is expected to end with "/". Ref webpack/webpack-hot-middleware#366 * feat(hot): add a building indicator with optional compilation progress (#2358) The client shows a small badge (shadow DOM, CSSOM-only styles) while a rebuild is in progress, enabled by default (disable with `?progress=false` on the client entry). When the server enables the new `hot.progress` option, webpack's ProgressPlugin publishes throttled `progress` events over SSE (deduplicated by rounded percent, reset on each new build) and the badge renders a CSP-safe SVG progress ring with the compilation percentage. The palette is shared with the error overlay through a common theme module, and the indicator is exposed as a standalone subpath export (webpack-dev-middleware/client/indicator) so other tooling can reuse it. Both the indicator and the overlay now bail out safely when `document.body` does not exist yet. Ref webpack/webpack-hot-middleware#167 * docs: add HMR notes and troubleshooting section (#2362) * docs: add HMR notes and troubleshooting section Covers the recurring questions from the webpack-hot-middleware backlog: browser connection limits and HTTP/2 (webpack/webpack-hot-middleware#423, webpack/webpack-hot-middleware#298), warning filtering layers (webpack/webpack-hot-middleware#319, webpack/webpack-hot-middleware#228), absolute paths and public paths (webpack/webpack-hot-middleware#281, webpack/webpack-hot-middleware#203, webpack/webpack-hot-middleware#193), and a custom-events recipe (webpack/webpack-hot-middleware#118, webpack/webpack-hot-middleware#226). The client `reload` option now also notes its default differs from webpack-hot-middleware (webpack/webpack-hot-middleware#370). * fixup! * fix(hot): publish sync instead of built for unchanged bundles (#2357) * fix(hot): publish sync instead of built for unchanged bundles With a MultiCompiler, rebuilding one child re-emits done for every child. Clients of the unchanged bundles then try to fetch a hot-update manifest that was never emitted (404 / "Cannot find update", or an unwanted full reload with reload=true). Compare each bundle's hash against the previous build and announce unchanged bundles as `sync` instead of `built`. The first build still publishes `built`, and new clients are still caught up with `sync` — but no longer while a rebuild is in progress. Ref webpack/webpack-hot-middleware#312 * fixup! * fix(client): reload when an accept handler errors during apply (#2360) * fix(client): reload when an accept handler errors during apply An error thrown inside a module.hot.accept handler was logged and ignored, leaving the page running stale code with no recovery. When the `reload` option is enabled (the default), onErrored now falls back to a full page reload, like the other unrecoverable update paths. The HMR runtime and page-reload calls are isolated behind small utils so process-update finally has direct test coverage. A missing HMR runtime is now reported with a single actionable error instead of throwing at bundle evaluation. Ref webpack/webpack-hot-middleware#334 * fixup! * fixup! * fix(hot): scope the catch-up sync and pair bundles by name - A client connecting after a build now receives the catch-up `sync` alone instead of it being broadcast to every connected client, which re-triggered their reporters on each new tab. - `publishBundles` pairs the previous build's bundles by name instead of array index, so a changing set of compilations cannot compare a bundle against a sibling's hash. Unnamed bundles keep the positional pairing. - The client's console dedup cache is cleared per bundle, so a sibling's clean build no longer re-logs another bundle's unchanged problems. * fix(hot): end SSE requests that arrive after close() instance.close() left `context.hot` set while `handle()` silently returned, so a request to the hot path after close received no response, no `next()`, and hung until the socket timed out. Detach the SSE intercept on close so requests fall through to the regular middleware, and answer 404 from `handle()` for requests that raced the intercept. * feat(client): share the building indicator and track builds by source (#2369) The badge state lives in a window singleton (same pattern as the overlay), so a second bundled copy of the module drives the same badge instead of stacking a duplicate, and fields missing from another version's state are filled in place. show() and hide() take an optional source: each concurrent build keeps the badge alive until every source finished, so in a MultiCompiler one bundle's `built` no longer hides the badge while a sibling is still compiling — and with two clients sharing the badge, one client's hide() cannot drop the other's indication. hide() without a source still removes the badge unconditionally. Progress payloads carry no name, so the client attributes them to the most recent `building` event. * feat(client): share the overlay across bundled copies and report by source (#2368) * feat(client): share the overlay across bundled copies and report by source The overlay DOM and problem state now live in a window singleton, so a second bundled copy of the module (e.g. the webpack-dev-server client once it adopts this overlay) renders into the same iframe instead of stacking a duplicate. Fields missing from a state created by an older copy are filled in place, and the Trusted Types policy moves into the shared state too — creating two policies with the same name throws under an enforced CSP. * test(overlay): prevent re-rendering when clearing a source that reported nothing * refactor(cspell): consolidate cspell configuration into .cspell.json and remove cspell.config.json * fix(hot): name building events and repair the client reconnect lifecycle The building payload now carries the name of the compilation that invalidated (tapped per child compiler, since the MultiCompiler hook does not say which one fired), so clients can pair it with the built or sync that follows — without it the building indicator registered every build under "" and could never be hidden for named compilations. On the client, the inactivity watchdog is restarted inside init(), so it survives a reconnect instead of dying with the first clearInterval, and the reconnect timeout handle is now stored and cleared by close(), so disconnect() during the reconnect window no longer resurrects an orphaned, uncloseable connection. All three defects were inherited from webpack-hot-middleware. * fix(hot): harden the SSE handshake and defer the overlay's Escape listener The handshake now ends a response whose headers were already sent instead of crashing on writeHead, and the middleware routes handshake errors to next() — an exception there previously became an unhandled rejection that killed the process. The overlay's Escape listener on the host document is now attached lazily inside ensureOverlay (once per page, through the shared state), matching how webpack-dev-server registers it inside createOverlay, so importing the client in a non-DOM environment (SSR bundle, worker) no longer throws at evaluation time. * fix(hot): align statsOptions and heartbeat validation and pair duplicate-name bundles by occurrence statsOptions now accepts only the object form everywhere: the schema rejected string presets the types allowed, and booleans validated but were silently ignored since toBundles only merges objects over the middleware's base stats options. Schema, JSDoc, and generated types all agree now, so invalid forms fail validation instead of at runtime. heartbeat: 0 was schema-valid but silently replaced with the 10s default by a falsy check — the schema now requires >= 1 and the code uses ?? so the option and its validation tell the same story. * fix(hot): require a leading slash in the hot path and serialize publishes once A path without a leading slash validated but could never match a request, since URL pathnames always start with one — the schema now enforces it. publish() also serializes the payload once per event instead of once per connected client. * fix(hono): set headers in the SSE writeHead shim instead of appending writeHead semantics replace a header an earlier middleware may have left on the response — appending merged both values into one comma-joined header (e.g. "max-age=100, no-cache, no-transform" for Cache-Control, which intermediaries may treat as cacheable). * docs: fix the hot option reference and add a webpack-hot-middleware migration guide The changeset advertised a `log` option that fails validation (it is `progress`), `hot.statsOptions` documented the boolean form the schema no longer accepts, `hot.path` now notes the required leading slash, and the launch-editor sentence that had landed inside the `paginate` parenthetical is back with `openEditorEndpoint`, where it belongs. Also adds a "Migrating from webpack-hot-middleware" section: server and webpack-config before/after, an option mapping table, and the programmatic API equivalents. * docs: make the migration guide's entry snippet valid standalone JS eslint parses the README's fenced code blocks, and the bare `entry: [...]` object fragment failed with "Unexpected token :" — the before/after entries are now wrapped in module.exports. * build: compile the client to ES5 and keep dist on the node target The shared preset-env targets (`esmodules` + node 0.12) also applied to `src`, so `dist` was downlevelled to ES5 with inlined regenerator helpers. Target node 20.9 by default and override only `client-src`, whose output has to be parsable by an old browser. `lint:types-client` now checks the client without node types and against an ES5 `lib`, so a post-ES5 built-in is an error instead of a runtime failure. * fix(package): do not add an exports field yet Adding `exports` hides every path the package does not list (deep imports like `webpack-dev-middleware/dist/...`), which is a breaking change. The `./client` subpath resolves through the directory anyway, so the field is dropped and a TODO records it for the next major. Also drops the now unused `@babel/plugin-transform-runtime` dev dependency. * refactor(client): define everything before it is used and stay on ES5 built-ins Drops the `no-use-before-define: { functions: false }` exception: the client is reordered so every function is defined before its first reference, which also removes the `@ts-expect-error`s that hoisting forced on process-update. Replaces the built-ins an ES5 browser does not have (`Map`, `URLSearchParams`, `Object.values`, `flatMap`, `includes`, `append`, `remove`) and guards `fetch`. A non-numeric `timeout` is now ignored (`NaN` never compares greater, so the watchdog could never report a dead connection) and `dynamicPublicPath=false` no longer behaves like `true`. * fix(hot): never write to a response that already ended The heartbeat and every publish wrote to each registered client, so a response ended between the socket dying and its `close` event threw a write-after-end. Clients are skipped once they end, and a request that was already destroyed when the handshake finished is dropped instead of staying in the client map forever (`close` never fires for it again). * ci: do not add a branch to the workflow triggers * test: keep the js3 array fixture output under test/outputs Its siblings write to `../outputs/one-error-one-warning-one-success`; `js3` wrote into `test/fixtures` and left an untracked directory behind after every run. * docs: list the hot options and note what the client needs in old browsers * test(client): assert the compiled client is always ES5 Compiles every `client-src` file through the real build config (`envName: "production"`, since jest runs under the test env) and walks the acorn AST against an allowlist of ES5.1 node types, so a syntax nobody thought of fails rather than slips through. `let`, generators, computed/shorthand properties, bigint literals and post-ES5 regular expression flags are checked on the node types ES5 already had. Only the module syntax webpack consumes (and the `import.meta.webpackHot` it replaces) is allowed through. * docs: list the client overlay options in their own table * test(hot): cover the remaining lines of the SSE stream Codecov reported three uncovered lines in src/hot.js. Two are branches the review added (the catch-up write to an ended response, and the idempotent disconnect), one is the child-compilation path of extractBundles. hot.js is at 100% lines from the unit test alone now. * fix(hot): address the automated review findings - Attach the SSE stream only for GET: a HEAD request to the hot path was handed a body and left hanging until it timed out. - Reject a query string or fragment in `hot.path`: `pathMatch` compares pathnames, so such an endpoint could never match a request. - Do not subscribe the same client copy twice: with `autoConnect` on, a `setOptionsAndConnect()` call added a second listener and every message was processed twice. Keyed by path, so a call that changes `path` still subscribes. - Keep delivering to `subscribeAll` when the `name` filter rejects an event, as its documentation promises. - Walk `querySelectorAll` by index: a NodeList is not iterable in an ES5 browser, so the compiled for...of threw there. - Say in the changeset that the client ships with the package rather than being served by the middleware. --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: alexander-akait <sheo13666q@gmail.com>
Summary
What kind of change does this PR introduce?
Did you add tests for your changes?
Does this PR introduce a breaking change?
If relevant, what needs to be documented once your changes are merged or what have you already documented?
Use of AI